// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved
// Developer:  Michael Antonio
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Reflection;

namespace MichMan.Utilities
{
    /// <summary>
    /// Some stuff that should be part of the framework, IMO.
    /// </summary>
    public static class EnumUtility
    {
        public static bool TestFlag<T>(this T value, T flag) where T : struct
        {
            return (Convert.ToInt32(value) & Convert.ToInt32(flag)) == Convert.ToInt32(flag);
        }

        public static bool TryParse<T>(string value, out T result) where T : struct
        {
            return TryParse<T>(value, true, out result);
        }

        public static bool TryParse<T>(string value, bool ignoreCase, out T result) where T : struct
        {
            return InternalTryParse<T>(value, ignoreCase, out result);
        }

        public static bool HasFlags<T>() where T : struct
        {
            return HasFlagsInternal<T>();
        }

        private static bool HasFlagsInternal<T>()
        {
            return typeof(T).GetTypeInfo().GetCustomAttributes(typeof(System.FlagsAttribute),
                false).Any();
        }

        internal static bool InternalTryParse<T>(string value, bool ignoreCase, out T result)
        {
            if (String.IsNullOrEmpty(value))
            {
                result = default(T);
                return false;
            }

            try
            {
                foreach (string s in InternalGetNames<T>())
                {
                    if (value.Equals(s, StringComparison.OrdinalIgnoreCase))
                    {
                        value = s;
                        break;
                    }
                }

                // DEVNOTE:  If the enum is flags, the IsDefined check is not definitive (there may be multiple values in the wrong case, which IsDefined does not allow but EnumParse does), 
                // and thus the EnumParse may still throw.
                // The GetNames check and IsDefined call are meant to reduce the amount of throwing that happens in the interest of performance.
                if (Enum.IsDefined(typeof(T), value) || HasFlagsInternal<T>())
                {
                    result = (T)Enum.Parse(typeof(T), value, ignoreCase);
                    return true;
                }
            }
            catch
            {
                result = default(T);
                return false;
            }

            result = default(T);
            return false;
        }

        public static IEnumerable<string> GetNames<T>() where T : struct
        {
            return InternalGetNames<T>();
        }

        private static IEnumerable<string> InternalGetNames<T>()
        {
            Type enumType = typeof(T);

            if (!enumType.GetTypeInfo().IsEnum)
            {
                throw new ArgumentException("Type '" + enumType.Name + "' is not an enum");
            }

            IEnumerable<string> names = from field in enumType.GetTypeInfo().DeclaredFields
                                    where field.IsLiteral
                                    select field.Name;

            return names;
        }

        public static IEnumerable<T> GetValues<T>() where T : struct
        {
            Type enumType = typeof(T);

            if (!enumType.GetTypeInfo().IsEnum)
            {
                throw new ArgumentException("Type '" + enumType.Name + "' is not an enum");
            }

            IEnumerable<T> fields = from field in enumType.GetTypeInfo().DeclaredFields
                                    where field.IsLiteral
                                    select (T)field.GetValue(enumType);

            return fields;
        }
    }
}

